Add file-level user metadata - #8740
Conversation
Adds optional file-level metadata to the Vortex file format: string-keyed, opaque byte segments referenced from the postscript. Keys and locators live in the postscript (read at open); values load only on `include_metadata`, resolved per-locator through the segment source (from the initial footer read when covered, else a targeted read) and copied out. A default open materializes nothing. `Footer` carries only locators, so a cached footer is identical regardless of the opener's flag and one file's metadata can't surface for another via the multi-file cache. `DType`, its FlatBuffers/protobuf serialization, and the scan path are untouched; the wire change is a single additive `Postscript` field (file version stays 1). `Alignment::from_exponent` on untrusted postscript input now returns an error instead of panicking (the TUI inspector included). Revives Nicholas Gates' file-metadata-segments work (#7954), rebased onto develop and reshaped so metadata never widens the footer read or pins its backing buffer. cargo build + fmt + clippy clean; vortex-file 100/100, vortex-buffer 822/822. Co-Authored-By: Nicholas Gates <nick@nickgates.com> Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
Merging this PR will not alter performance
Comparing Footnotes
|
Move user metadata out of the postscript. Instead of per-key [PostscriptMetadata] locators embedded in the postscript, the postscript now carries one optional locator to a FileMetadata segment (keyed opaque values) that may live anywhere in the file. Keys no longer pressure the postscript, so the count/length limits are dropped. A default open reads no metadata; include_metadata resolves the single segment in one read. Addresses review feedback on the original approach. Co-Authored-By: Nicholas Gates <nick@nickgates.com> Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
|
ok @mprammer sorry for back and forth. I think I remembered wrongly what this pr originally did. We can put the extra 512 bytes in the postscript for the keys. We should make sure 32 bytes covers most of the query engine keys |
This reverts the single-segment rework (a9c2230), restoring the original keys-in-postscript approach per review consensus. Re-applies the CI lint fixes that the rework had carried (alignment cast, parking_lot::Mutex, to_vec) so the restored design stays green. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
Signed-off-by: mprammer <martin@spiraldb.com> # Conflicts: # vortex-file/src/footer/deserializer.rs # vortex-file/src/footer/mod.rs
Polar Signals Profiling ResultsLatest Run
Previous Runs (2)
Powered by Polar Signals Cloud |
Benchmarks: Vortex queries 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.986x ➖, 0↑ 0↓)
datafusion / parquet (0.872x ✅, 2↑ 0↓)
duckdb / vortex-file-compressed (0.910x ➖, 1↑ 0↓)
duckdb / parquet (0.984x ➖, 0↑ 0↓)
No file size changes detected. |
32 bytes covered short Iceberg-style keys but not reverse-DNS query-engine keys such as `org.apache.spark.sql.parquet.row.metadata` (41 bytes), which Spark writes into every Parquet file. 64 bytes clears these; with 16 keys the postscript key budget stays at 1 KiB. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
The boundary round-trip test builds the largest possible metadata postscript (max segments, one max-length key); assert its serialized size stays within MAX_POSTSCRIPT_SIZE so the footer is always covered by the initial tail read. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
Build the true worst case (every key at the max length) and assert the total metadata size alongside the total postscript size, so all keys are exercised at the boundary rather than just one. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: mprammer <martin@spiraldb.com>
Signed-off-by: mprammer <martin@spiraldb.com> # Conflicts: # vortex-file/src/footer/postscript.rs # vortex-file/src/footer/segment.rs # vortex-file/src/open.rs
…nks, Python goldens The develop merge brought in the file-level metadata tests (#8740), which write with bare sessions that enable no editions and now fail validation. Enable the crate's test edition in those sessions, matching the other vortex-file unit tests. Also re-qualify the vortex::editions module-doc links (module docs resolve at the lib.rs `pub mod` site) and update the Python doctest golden file sizes for the edition-derived footer context. Signed-off-by: "Joe Isaacs" <joe.isaacs@live.co.uk> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CK2nCuXnyd2g3mNHQC8Lz2
…nks, Python goldens The develop merge brought in the file-level metadata tests (#8740), which write with bare sessions that enable no editions and now fail validation. Enable the crate's test edition in those sessions, matching the other vortex-file unit tests. Also re-qualify the vortex::editions module-doc links (module docs resolve at the lib.rs `pub mod` site) and update the Python doctest golden file sizes for the edition-derived footer context. Signed-off-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CK2nCuXnyd2g3mNHQC8Lz2
Adds optional file-level metadata to the Vortex file format: a set of string-keyed, opaque byte segments referenced from the postscript, for consumers that need to attach identity or annotation to a file (Iceberg field IDs, an Arrow-metadata round-trip, provenance). Keys and their segment locators live in the postscript and are read at open; the opaque values load only when a reader opts in with
include_metadata, resolved one locator at a time through the file's existing segment source — served from the initial footer read when they fall inside it, otherwise a targeted read. A default open never materializes them and makes no metadata-driven read. TheDType, its FlatBuffers and protobuf serialization, and the scan path are unchanged; the wire change is a single additivePostscriptfield, so old readers skip it and the file version stays1.This revives #7954 (the original file-metadata-segments work) rebased onto develop, with the read path reshaped so metadata is never folded into the footer's contiguous tail read and never keeps that buffer alive: values are resolved per-locator and copied out.
Footercarries only the locators, so a cached footer is identical whether or not the opener asked for metadata, and one file's metadata can't surface for another through the multi-file cache. Parsing a segment alignment from an untrusted postscript now returns an error rather than panicking on an out-of-range exponent (the TUI inspector included). The public read API is additive —VortexFilegains metadata accessors andVortexOpenOptionsaninclude_metadataopt-in — andFooter::newis unchanged; the generatedPostscriptFlatBuffer gains a field, as any additive schema change does.🤖 Generated with Claude Code